function SpUpload(clientName, textId, fileUploadId, buttonId, exts, unknow, supportExt, chunk) {

  var _slides = 0;
  var _uploadServerFileName = "";
  var _bytesPerChunk = chunk; //1024 * 512; // 1/2 mb
  var _hashParameter = Library.WebControl.Hashtable.CreateHashtable();
  var _instance = this;
  var _textId = textId;
  var _fileUploadId = fileUploadId;
  var _buttonId = buttonId;
  var _exts = new Array(); //exts.split(';');

  if (exts != "") _exts = exts.split(';');
  var _unknow = unknow;
  var _supportExt = supportExt;
  var _util = Library.WebControl.Util;
  var _textChanged = null;
  var _beforeCallback = null;
  var _partFileUploadComplete = null;
  _globalHashObject.Add(clientName, _instance);
  window[clientName] = _instance;

  function getTextControl() {
    return _util.GetObjectById(_textId);
  };
  function getFileUploadControl() {
    return _util.GetObjectById(_fileUploadId);
  };
  function validateExt() {
    var file = _instance.GetText();
    var li = file.lastIndexOf(".");
    if (li < 0) {
      alert(_unknow);
    } else {
      if (_exts.length == 0)
        return true;
      var ext = file.substring(li + 1, file.length);
      ext = ext.toLowerCase();
      for (var i = 0; i < _exts.length; i++) {
        if (_exts[i] == ext)
          return true;
      }
    }
    alert(_supportExt + " " + _exts.join(';'));
    return false;
  };
  function fileChange() {
    var txt = getTextControl();
    var ov = txt.value;
    var nv = getFileUploadControl().value;
    if (ov != nv) {
      txt.value = nv;
      if (_textChanged != null)
        _textChanged(_instance, {
          OldText: ov,
          NewText: nv
        });
    }

  };

  this.Click = function() {
    getFileUploadControl().click();
  };


  this.GetText = function() {
    return getTextControl().value;
  };
  this.GetValue = function() {
    return _instance.GetText();
  };

  this.AddParameter = function(a, b) {
    _hashParameter.Add(a, b);
  };

  function mouseMove(e) {
    var fu = getFileUploadControl();
    var mos = _util.GetMousePosition(e);
    //var buttonObj = _util.GetObjectById(_buttonId);
    var bpos = _util.GetPosition(_buttonId);
    //var bpos = _util.GetPosition(buttonObj.parentNode.parentNode);
    var bsiz = _util.GetSize(_buttonId);
    if (bpos.X <= mos.X && bpos.X + bsiz.W >= mos.X) {
      if (bpos.Y <= mos.Y && bpos.Y + bsiz.H >= mos.Y) {
        //var parrentAbs =null; //_util.GetAbsParrent(fu);
        //if(parrentAbs==null){
        fu.style.top = (mos.Y - bpos.Y - 10) + "px";
        fu.style.left = (mos.X - bpos.X - 50) + "px";
        //}
        //else{
        //var psSize = _util.GetPosition(parrentAbs);
        //fu.style.top = (mos.Y - 10 - psSize.Y) + "px";
        //fu.style.left = (mos.X - 50 - psSize.X) + "px";
        //}
        return;
      }
    }
    fu.style.top = "-100px";
  };
  this.SupportChunk = function() {
    return blobBuilderSupported();
  };
  function blobBuilderSupported() {
    if (window.BlobBuilder) {
      // No change needed - the W3C standard API will be used by default.
      return true;
    }
    else if (window.MSBlobBuilder) {
      window.BlobBuilder = window.MSBlobBuilder;
    }
    else if (window.WebKitBlobBuilder) {
      window.BlobBuilder = window.WebKitBlobBuilder;
    }
    else if (window.MozBlobBuilder) {
      window.BlobBuilder = window.MozBlobBuilder;
    } else if (window.Blob) {
      window.BlobBuilder = window.Blob;
    }
    else {
      return false;
    } // if-else

    return true;
  };  // BlobBuilderSupported
  function uploadWithChunk(p, f) {
    _uploadServerFileName = "";
    var blob = document.getElementById(_fileUploadId).files[0];
    var start = 0;
    var end;
    var index = 0;
    //var slices; // slices, value that gets decremented
    //var slicesTotal; // total amount of slices, constant once calculated
    // calculate the number of slices
    if (blob.size <= _bytesPerChunk) return false; // let normal upload
    _slides = Math.ceil(blob.size / _bytesPerChunk);
    //slicesTotal = slices;
    if (start < blob.size) {
      end = start + _bytesPerChunk;
      if (end > blob.size) {
        end = blob.size;
      }

      uploadFile(blob, index, start, end, p, f);
      //start = end;
      //index++;
    }
    return true;
  };




  /**
  * Performs actual upload, adjustes progress bars
  *
  * @param blob
  * @param index
  * @param start
  * @param end
  */
  function uploadFile(blob, index, start, end, p, f) {
    var xhr;
    var end;
    var chunk;

    xhr = Library.WebControl.Ajax.HttpRequest();  //new XMLHttpRequest();

    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        if (xhr.status == 200) {
          _slides--;

          var rep = _util.ParseAjaxResponse(xhr.responseText);
          if (rep.IsValid) {
            start = end;
            index++;
            _uploadServerFileName = rep.Result;
            if (_partFileUploadComplete != null) {
              _partFileUploadComplete(_instance, { UploadedTimes: index, RemainTimes: _slides, FileSize: blob.size, UploadedSize: end });
            }
            // if we have finished all slices
            if (_slides == 0) {
              setTimeout(function() { chunkFinalUpload(p, f); }, 100);
            } else {

              if (start < blob.size) {
                end = start + _bytesPerChunk;
                if (end > blob.size) {
                  end = blob.size;
                }
                setTimeout(function() { uploadFile(blob, index, start, end, p, f); }, 100);
              }
            }
          } else {
            alert(rep.Result);
          }
        }
      }
    };

    if (blob.webkitSlice) {
      chunk = blob.webkitSlice(start, end);
    } else if (blob.mozSlice) {
      chunk = blob.mozSlice(start, end);
    } else {
      chunk = blob.slice(start, end);
    }

    //    xhr.addEventListener("load", function(evt) {
    //      var percentageDiv = document.getElementById("percent");
    //      var progressBar = document.getElementById("progressBar");

    //      progressBar.max = progressBar.value = 100;
    //      percentageDiv.innerHTML = "100%";
    //    }, false);



    var url = _spglobalRequestUrl;
    var parm = "__SPUPLOADIP_=T&__SPUPLOADRQP_=T&__SPAXRQ_=T&__SPUPLOADS_=" + _fileUploadId + "clone&__SPUPLOADFN_=" + _uploadServerFileName;
    if (url.indexOf("?") >= 0) {
      url = url + "&" + parm
    } else {
      url = url + "?" + parm;
    }
    xhr.open("POST", url, true);

    var fd = new FormData();

    fd.append("name", blob.name);

    //fd.append("index", blob.index);

    fd.append("file", chunk);
    //xhr.send(chunk); // but if we support slice() everything should be ok
    xhr.send(fd);
  };

  //  function str2ab_blobreader(str, callback) {
  //    var blob;
  //    if (typeof (BlobBuilder) !== 'undefined') {
  //      var bb = new BlobBuilder();
  //      bb.append(str);
  //      blob = bb.getBlob();
  //    } else {
  //      blob = new Blob([str]);
  //    }
  //    var f = new FileReader();
  //    f.onload = function(e) {
  //      callback(e.target.result)
  //    }
  //    f.readAsArrayBuffer(blob);
  //  };

  function chunkFinalUpload(p, f) {
    var req = Library.WebControl.Ajax.CreateRequest();
    if (_beforeCallback != null) _beforeCallback(this, {});
    initUploadHeader(p);
    _hashParameter.Add("__SPUPLOADCFN_", _instance.GetText());
    req.SetData(_hashParameter.ToQuery());
    removeUploadHeader();
    req.SendCallback(_instance, function(s, e) {
      var result = _util.ParseAjaxResponse(e.ResponseText);
      if (!result.IsValid) alert(result.Result);
      if (f != null) f(_instance, result);
    });
  };



  function initUploadHeader(p) {
    _hashParameter.Add("__SPUPLOADRQP_", "T");
    _hashParameter.Add("__SPAXRQ_", "T");
    _hashParameter.Add("__SPUPLOADS_", _fileUploadId + "clone");
    _hashParameter.Add("__SPUPLOADCP_", encodeURIComponent(p));
    _hashParameter.Add("__SPUPLOADFN_", _uploadServerFileName);

  };
  function removeUploadHeader() {
    //    _hashParameter.Remove("__SPUPLOADRQP_");
    //    _hashParameter.Remove("__SPUPLOADS_");
    //    _hashParameter.Remove("__SPUPLOADCP_");
    //    _hashParameter.Remove("__SPAXRQ_");
    _hashParameter.Clear();
  };




  this.Upload = function(p, uploadCompleteFunction) {
    if (!validateExt()) return;
    if (blobBuilderSupported()) {
      if (uploadWithChunk(p, uploadCompleteFunction))
        return;
      // let normal upload
    }


    var t = new Date().getTime();
    var uf = createForm(t, _fileUploadId);
    var uif = createFrame(t);
    if (_beforeCallback != null) _beforeCallback(this, {});
    initUploadHeader(p);
    var kvs = _hashParameter.GetAll();
    for (var i = 0; i < kvs.length; i++) {
      var id = kvs[i].Id;
      var value = kvs[i].Value;
      var ip = document.createElement('input');
      ip.setAttribute("type", "hidden");
      ip.setAttribute("id", id);
      ip.setAttribute("name", id);
      ip.setAttribute("value", value);
      uf.appendChild(ip);
    }
    removeUploadHeader();
    if (window.attachEvent) {
      uif.attachEvent('onload', function() {
        fileUploadComplete(uif, uf, p, uploadCompleteFunction);
      });
    }
    else {
      uif.addEventListener('load', function() {
        fileUploadComplete(uif, uf, p, uploadCompleteFunction);
      }, false);
    }
    uf.setAttribute("target", uif.id);
    uf.setAttribute("action", _spglobalRequestUrl)
    uf.setAttribute("method", "POST");
    if (uf.encoding) {
      uf.encoding = 'multipart/form-data';
    }
    else {
      uf.enctype = 'multipart/form-data';
    }
    uf.submit();
  };
  function createForm(formId, fileUploadId) {
    formId = "spUploadForm" + formId;
    var newFileUploadId = "spUploadFile" + fileUploadId;
    var f = document.createElement("form");
    //f.setAttribute("method", "POST");
    f.setAttribute("name", formId);
    f.setAttribute("id", formId);
    //f.setAttribute("action", _spglobalRequestUrl);
    f.setAttribute("enctype", "multipart/form-data");
    f.style.position = "absolute";
    f.style.top = "-1000px";
    f.style.left = "-1000px";
    var file = document.getElementById(fileUploadId);
    var newFile = file.cloneNode(true);
    file.setAttribute("id", newFileUploadId);
    file.parentNode.insertBefore(newFile, file);
    f.appendChild(file);
    document.body.appendChild(f);
    return f;
  };

  function createFrame(frameId) {
    frameId = "spFrame" + frameId;
    var fr = null;
    fr = document.createElement('iframe');
    fr.id = frameId;
    fr.name = frameId;
    if (window.ActiveXObject) {
      //var fr = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
      //if (typeof uri == 'boolean') {
      fr.src = 'javascript:false';
      //}
      //else if (typeof uri == 'string') {
      //fr.src = uri;
      //}
    }
    //        else {
    //          
    //        }
    // document.createElement("iframe");
    //fr.setAttribute("name", frameId);
    //fr.setAttribute("id", frameId);
    fr.style.position = "absolute";
    fr.style.top = "-800px";
    fr.style.left = "-800px";
    //fr.src ="javascript:false";
    document.body.appendChild(fr);
    return fr;
  };
  function fileUploadComplete(io, uf, p, f) {
    _hashParameter.Clear();
    var xml = {
      Parameter: p
    };
    try {
      var response = null;
      if (io.contentWindow) {
        response = io.contentWindow.document.body ? io.contentWindow.document.body.innerHTML : null;


      }
      else
        if (io.contentDocument) {
        response = io.contentDocument.document.body ? io.contentDocument.document.body.innerHTML : null;

      }
      var xml = null;
      if (response != null) {
        xml = _util.ParseAjaxResponse(response);
      }
      if (xml == null)
        xml = { IsValid: false, Result: "Nothing response from server" };
      if (!xml.IsValid) alert(xml.Result);
      xml.Parameter = p;
      f(_instance, xml);
      setTimeout(function() {
        io.parentNode.removeChild(io);
        uf.parentNode.removeChild(uf);
        getTextControl().value = "";
      }, 200);

    }
    catch (e) {
      alert(e);
    }
  };

  this.AddHandler = function(t, f) {
    switch (t) {
      case 1:
        _textChanged = f;
        break;
      case 3:
        _beforeCallback = f;
        break;
      case 4:
        _partFileUploadComplete = f;
        break;
    }
  };
  this.Call = function(t, p) {
    switch (t) {
      case 1: // filechange
        fileChange();
        break;
      case 2:
        mouseMove(p);
        break;
    }
  };

};
